﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Topro.Service
{
    using NLog;
    using System.Threading;
    using Topro.Application;
    using Topro.Entity;
    using Topro.Entity.Enum;
    using Topro.Entity.Query;
    using Topro.Entity.Response;
    using Topro.Entity.Table;
    using Topro.Enum;
    using Topro.Monitor;

    /// <summary>
    /// 堆垛机
    /// </summary>
    public class TwoStacker
    {
        #region 变量
        /// <summary>
        /// 日志委托
        /// </summary>
        public delegate void InfoDelegate(int type, Dictionary<string, string> dc);

        /// <summary>
        /// 日志事件
        /// </summary>
        public event InfoDelegate infoEvent;

        /// <summary>
        /// 站台消息委托
        /// </summary>
        public delegate void StationDelegate(int station, int status);

        /// <summary>
        /// 站台消息事件
        /// </summary>
        public event StationDelegate stationEvent;

        /// <summary>
        /// 剁机线程
        /// </summary>
        public Thread stackerThread = null;

        /// <summary>
        /// 入库暂存站台
        /// </summary>
        public List<StockStation> inStockStations = null;

        /// <summary>
        /// 出库暂存站台
        /// </summary>
        public List<StockStation> outStockStations = null;

        /// <summary>
        /// 暂存站台
        /// </summary>
        public List<StockStation> stockStations = null;

        /// <summary>
        /// 入库队列
        /// </summary>
        Queue<int> inQueue = new Queue<int>();

        /// <summary>
        /// 出库队列
        /// </summary>
        Queue<int> outQueue = new Queue<int>();

        /// <summary>
        /// 剁机帮助类
        /// </summary>
        public PlcUtil stackerUtil = null;

        /// <summary>
        ///  输送线帮助类
        /// </summary>
        public PlcUtil transLineUtil = null;

        /// <summary>
        /// 工作类型
        /// </summary>
        private OutInStatus outinStatus = OutInStatus.入库;

        /// <summary>
        /// 
        /// </summary>
        public List<DeviceExceptionCodeResponse> deviceExceptionCode;

        /// <summary>
        /// 堆垛机
        /// </summary>
        public int stackerStationNumber;

        /// <summary>
        /// 输送线
        /// </summary>
        public int transLineStationNumber;

        /// <summary>
        /// 
        /// </summary>
        public string warehouseCode;

        /// <summary>
        /// 脉冲状态
        /// </summary>
        public int pulseStatus = 0;

        /// <summary>
        /// 日志
        /// </summary>
        private static readonly Logger logStacker = LogManager.GetLogger("Stacker");
        #endregion

        #region 主流程
        /// <summary>
        /// 开始堆垛机业务
        /// </summary>
        public void main()
        {
            while (true)
            {
                try
                {
                    Thread.Sleep(1000);
                    //序列号[D617]
                    var serialNo = stackerUtil.Get(StockAddr.SerialNo);
                    //是否空闲[D100]
                    var free = stackerUtil.Get(StockAddr.IsFree);
                    //工作中[D101]
                    var work = stackerUtil.Get(StockAddr.Work);
                    //异常[D120]
                    var exception = stackerUtil.Get(StockAddr.Exception);
                    //入库完成[D103]
                    var inDone = stackerUtil.Get(StockAddr.InDone);
                    //出库完成[D104]
                    var outDone = stackerUtil.Get(StockAddr.OutDone);

                    if (inStockStations != null && inStockStations.Any())
                    {
                        inStockStations.ForEach(item => {
                            var exist = transLineUtil.Get(item.HaveTrayAddr);

                            if (exist == (int)WorkStatus.One)
                                stationEvent(item.Id, 1);
                            else
                                stationEvent(item.Id, 0);
                        });
                    }
                    //脉冲
                    if (pulseStatus == (int)WorkStatus.Zero)
                    {
                        pulseStatus = 1;
                        stackerUtil.Set(StockAddr.PulseAddr, (int)1);
                    }
                    else if (pulseStatus == (int)WorkStatus.One)
                    {
                        pulseStatus = 0;
                        stackerUtil.Set(StockAddr.PulseAddr, (int)0);
                    }
                    //入库完成
                    this.InDone();
                    //出库完成
                    this.OutDone();
                    //异常
                    if (exception == (int)Success.Two)
                    {
                        infoEvent(23, new Dictionary<string, string> { ["1"] = $"异    常." });

                        for (int i = 2; i < 9; i++)
                        {
                            GetDeviceException(deviceExceptionCode, i);
                        }

                        continue;
                    }
                    else
                    {

                        if (free == (int)Success.Two)
                        {
                            if (outinStatus == OutInStatus.入库)
                            {
                                infoEvent(21, new Dictionary<string, string> { ["1"] = $"入    库." });
                                if (serialNo <= 0)
                                    this.In();
                                else
                                {
                                    stackerUtil.Set(StockAddr.SerialNo, (Int16)0);

                                    infoEvent(0, new Dictionary<string, string> { ["0"] = $"[二号垛机{StockAddr.SerialNo}流水号未清除]" });
                                }
                                outinStatus = OutInStatus.出库;
                            }
                            else if (outinStatus == OutInStatus.出库)
                            {
                                infoEvent(22, new Dictionary<string, string> { ["1"] = $"出    库." });
                                if (serialNo <= 0)
                                    this.Out();
                                else
                                {
                                    stackerUtil.Set(StockAddr.SerialNo, (Int16)0);

                                    infoEvent(0, new Dictionary<string, string> { ["0"] = $"[二号垛机{StockAddr.SerialNo}流水号未清除]" });
                                }
                                outinStatus = OutInStatus.入库;
                            }
                        }
                        else if (work == (int)Success.Two)
                            infoEvent(24, new Dictionary<string, string> { ["1"] = $"工 作 中" });
                        else
                            infoEvent(25, new Dictionary<string, string> { ["1"] = $"半 自 动" });
                    }
                }
                catch (ThreadAbortException ex)
                {
                    infoEvent(0, new Dictionary<string, string> { ["1"] = $"{ex.Message}." });
                }
                catch (Exception ex)
                {
                    infoEvent(0, new Dictionary<string, string> { ["1"] = $"异常{ex.Message}." });
                }
            }
        }
        #endregion

        #region 出入库

        #region 入库
        /// <summary>
        /// 入库
        /// </summary>
        private void In()
        {
            try
            {
                #region get
                //查询条件
                var query = new ASRSJOBQuery()
                {
                    Sql = "SELECT * from asrsjob WHERE TYPE='In' AND (ORIPORT LIKE '2-11-01-07' or ORIPORT LIKE '2-11-01-08' or ORIPORT LIKE '2-11-01-10')  and (TARPORT LIKE '2-11-01-03%' or TARPORT LIKE '2-11-01-04%') And STATE in(0,1) ORDER BY ID ASC LIMIT 0,1"
                };
                //获取指令
                var asrsjob = ASRSJOBApp.Instance.GetSingleASC(query);
                //指令对象是否为空
                if (asrsjob == null)
                    return;
                if (asrsjob.STATE == (int)WorkStatus.One)
                    return;
                #endregion

                #region from
                if (string.IsNullOrEmpty(asrsjob.ORIPORT))
                    infoEvent(0, new Dictionary<string, string> { ["0"] = $"[入库] [指令错误(from):{asrsjob.ORIPORT}为空]" });
                var oriport = asrsjob.ORIPORT.Split('-');
                //验证长度
                if (oriport.Length < 4)
                    infoEvent(0, new Dictionary<string, string> { ["0"] = $"[入库] [指令错误(from):{asrsjob.TARPORT}长度验证失败]" });
                //入库站台
                var fromStationId = int.Parse(oriport[3]);
                //入库站台对象
                var stockStation = inStockStations.FirstOrDefault(c => c.Id == fromStationId);
                //站台验证
                if (stockStation == null)
                {
                    infoEvent(0, new Dictionary<string, string> { ["0"] = $"[入库] [站台({fromStationId})查找失败]" }); return;
                }
                //入库状态
                var fromHaveTrayStatus = transLineUtil.Get(stockStation.HaveTrayAddr);
                //入库流水
                var fromSerialNo = transLineUtil.Get(stockStation.SerialNoAddr);
                //是否被占用、是否给流水号
                if (fromHaveTrayStatus == (int)Success.Zero)
                {
                    infoEvent(0, new Dictionary<string, string> { ["0"] = $"[入库] [站台({fromStationId})无载]" }); return;
                }
                else
                {
                    if (fromSerialNo <= 0)
                    {
                        infoEvent(0, new Dictionary<string, string> { ["0"] = $"[入库] [站台({fromStationId})无流水号]" }); return;
                    }
                    //模式
                    if (stockStation.Id == (int)WorkStatus.Ten)
                    {
                        //出入库站台类型 入(1)、出(2)
                        var fromStationType = transLineUtil.Get(stockStation.StationTypeAddr);
                        if (fromStationType != (int)WorkStatus.One)
                        {
                            infoEvent(0, new Dictionary<string, string> { ["0"] = $"[入库] [站台({fromStationId})上位机未切换为入库模式]" }); return;
                        }
                        //托盘类型(小(1)、大(2))
                        var fromPalletType = transLineUtil.Get(stockStation.PalletTypeAddr);
                        if (asrsjob.CTNTYPE != fromPalletType)
                        {
                            if (asrsjob.CTNTYPE == (int)WorkStatus.One)
                                infoEvent(0, new Dictionary<string, string> { ["0"] = $"[入库] [任务为小托盘]" });
                            else
                                infoEvent(0, new Dictionary<string, string> { ["0"] = $"[入库] [任务为大托盘]" });

                            if (fromPalletType == (int)WorkStatus.One)
                                infoEvent(0, new Dictionary<string, string> { ["0"] = $"[入库] [上位机判断为小托盘]" });
                            else if (fromPalletType == (int)WorkStatus.Two)
                                infoEvent(0, new Dictionary<string, string> { ["0"] = $"[入库] [上位机判断为大托盘]" });
                            else
                                infoEvent(0, new Dictionary<string, string> { ["0"] = $"[入库] [上位机未给托盘类型]" });

                            infoEvent(0, new Dictionary<string, string> { ["0"] = $"[入库] [站台({fromStationId})托盘类型与实际任务类型不一致]" }); return;
                        }
                    }
                }
                var from = new From();
                //排
                from.Pai = stockStation.Pai;
                //列
                from.Lie = stockStation.Lie;
                //层
                from.Ceng = stockStation.Ceng;
                #endregion

                #region to
                var to = new To();

                //1-10-01-020305
                var tarport = asrsjob.TARPORT.Split('-');
                //验证长度
                if (tarport.Length < 4)
                {
                    infoEvent(0, new Dictionary<string, string> { ["0"] = $"[入库] [指令错误:{asrsjob.TARPORT}]" }); return;
                }
                //分割
                var toArray = System.Text.RegularExpressions.Regex.Split(tarport[3], @"(?<=\G.{2})(?!$)");
                //排
                to.Pai = int.Parse(toArray[0]);
                //转换
                switch (to.Pai)
                {
                    case 3:
                        to.Pai = 1;
                        break;
                    case 4:
                        to.Pai = 2;
                        break;
                }
                //列
                to.Lie = int.Parse(toArray[1]);
                //层
                to.Ceng = int.Parse(toArray[2]);
                #endregion

                #region write
                var data = new Int16[8];

                data[0] = (Int16)from.Pai;
                data[1] = (Int16)from.Lie;
                data[2] = (Int16)from.Ceng;


                data[3] = (Int16)to.Pai;
                data[4] = (Int16)to.Lie;
                data[5] = (Int16)to.Ceng;

                data[6] = (Int16)asrsjob.Id;

                if (asrsjob.CTNTYPE == (int)WarehouseUnitType.小托盘)
                    data[7] = 1;
                else
                    data[7] = 2;
                //写入PLC
                var insertStatus = stackerUtil.Set(StockAddr.FromPai, 8, data);
                //休眠
                Thread.Sleep(1000);
                //流水号
                var serialNo = stackerUtil.Get(StockAddr.SerialNo);
                //源排
                var fromPai = stackerUtil.Get(StockAddr.FromPai);
                // 验证是否真证写入成功
                if (insertStatus == (int)WorkStatus.Zero && serialNo == asrsjob.Id && fromPai > 0)
                {
                    //工作中
                    stackerUtil.Set(StockAddr.Work, (Int16)2);
                    //是否空闲
                    stackerUtil.Set(StockAddr.IsFree, (Int16)0);

                    infoEvent(0, new Dictionary<string, string> { ["0"] = $"[入库] [单据号:{asrsjob.BINDID} 入库库位:{asrsjob.ORIPORT}]" });

                    infoEvent(0, new Dictionary<string, string> { ["0"] = $"[入库] [托盘号:{asrsjob.CTNCODE}]" });

                    infoEvent(0, new Dictionary<string, string> { ["0"] = $"[入库] [流水号:{serialNo}]" });
                    //界面日志
                    infoEvent(0, new Dictionary<string, string> { ["0"] = $"[入库] [写入指令成功.]" });

                    infoEvent(0, new Dictionary<string, string> { ["0"] = $"[入库] [FROM:{from.Pai}-{from.Lie}-{from.Ceng} TO:{to.Pai}-{to.Lie}-{to.Ceng}]" });
                    //更新
                    var updateResult = ASRSJOBApp.Instance.UpdateStatus(asrsjob.Id, (int)WorkStatus.One, (int)CompleteMode.自动);
                    if (updateResult.Success)
                        infoEvent(0, new Dictionary<string, string> { ["0"] = $"[入库] [更新指令状态成功.]" });
                    else
                        infoEvent(0, new Dictionary<string, string> { ["0"] = $"[入库] [更新状态失败:{updateResult.Message}]" });
                }
                else
                {
                    //重试机制
                    while (serialNo == (int)WorkStatus.Zero)
                    {
                        infoEvent(0, new Dictionary<string, string> { ["0"] = $"[入库] [写入PLC失败、正在重试.]" });
                        //重新写入PLC
                        insertStatus = stackerUtil.Set(StockAddr.FromPai, 8, data);
                        //休眠
                        Thread.Sleep(1000);
                        //重新获取排
                        serialNo = stackerUtil.Get(StockAddr.SerialNo);
                        //源排
                        fromPai = stackerUtil.Get(StockAddr.FromPai);
                        // 验证是否真证写入成功
                        if (insertStatus == (int)WorkStatus.Zero && serialNo == asrsjob.Id && fromPai > 0)
                        {
                            //工作中
                            stackerUtil.Set(StockAddr.Work, (Int16)2);
                            //是否空闲
                            stackerUtil.Set(StockAddr.IsFree, (Int16)0);

                            infoEvent(0, new Dictionary<string, string> { ["0"] = $"[入库] [单据号:{asrsjob.BINDID} 入库库位:{asrsjob.ORIPORT}]" });

                            infoEvent(0, new Dictionary<string, string> { ["0"] = $"[入库] [托盘号:{asrsjob.CTNCODE}]" });

                            infoEvent(0, new Dictionary<string, string> { ["0"] = $"[入库] [流水号:{serialNo}]" });

                            infoEvent(0, new Dictionary<string, string> { ["0"] = $"[入库] [写入指令成功.]" });

                            infoEvent(0, new Dictionary<string, string> { ["0"] = $"[入库] [FROM:{from.Pai}-{from.Lie}-{from.Ceng} TO:{to.Pai}-{to.Lie}-{to.Ceng}]" });
                            //更新
                            var updateResult = ASRSJOBApp.Instance.UpdateStatus(asrsjob.Id, (int)WorkStatus.One, (int)CompleteMode.自动);
                            if (updateResult.Success)
                                infoEvent(0, new Dictionary<string, string> { ["0"] = $"[入库] [更新指令状态成功.]" });
                            else
                                infoEvent(0, new Dictionary<string, string> { ["0"] = $"[入库] [更新状态失败:{updateResult.Message}]" });
                        }
                        //休眠
                        Thread.Sleep(2000);
                    }
                }
                #endregion
            }
            catch (Exception ex)
            {
                //界面日志
                infoEvent(0, new Dictionary<string, string> { ["0"] = $"[入库] [异常:{ex.Message}]" });
            }
        }
        #endregion

        #region 出库
        /// <summary>
        /// 出库
        /// </summary>
        private void Out()
        {
            try
            {
                #region get
                //查询条件
                //开始 ORIPORT
                //结束 TARPORT 
                var query = new ASRSJOBQuery()
                {
                    Sql = "SELECT * from asrsjob WHERE  TYPE='Out' AND (ORIPORT LIKE '2-11-01-03%' or ORIPORT LIKE '2-11-01-04%') and (TARPORT LIKE '2-11-01-06' or TARPORT LIKE '2-11-01-09' or TARPORT LIKE '2-11-01-10') And STATE in(0,1) ORDER BY ID ASC LIMIT 0,1"
                };
                //获取指令
                var asrsjob = ASRSJOBApp.Instance.GetSingleASC(query);
                //指令对象是否为空
                if (asrsjob == null)
                    return;
                if (asrsjob.STATE == (int)WorkStatus.One)
                    return;
                #endregion

                #region from
                //1-10-01-020305
                if (string.IsNullOrEmpty(asrsjob.ORIPORT))
                {
                    infoEvent(0, new Dictionary<string, string> { ["0"] = $"[出库] [FROM为空]" }); return;
                }
                //分割
                var tarport = asrsjob.ORIPORT.Split('-');
                //验证长度
                if (tarport.Length < 4)
                    infoEvent(0, new Dictionary<string, string> { ["0"] = $"[出库] [指令错误:{asrsjob.TARPORT}]" });
                //分割
                var fromArray = System.Text.RegularExpressions.Regex.Split(tarport[3], @"(?<=\G.{2})(?!$)");

                var from = new From();
                //排
                from.Pai = int.Parse(fromArray[0]);
                //转换
                switch (from.Pai)
                {
                    case 3:
                        from.Pai = 1;
                        break;
                    case 4:
                        from.Pai = 2;
                        break;
                }
                //列
                from.Lie = int.Parse(fromArray[1]);
                //层
                from.Ceng = int.Parse(fromArray[2]);
                #endregion

                #region to
                if (string.IsNullOrEmpty(asrsjob.TARPORT))
                {
                    infoEvent(0, new Dictionary<string, string> { ["0"] = $"[出库] [指令错误(from):{asrsjob.TARPORT}为空]" }); return;
                }
                var oriport = asrsjob.TARPORT.Split('-');
                //验证长度
                if (oriport.Length < 4)
                {
                    infoEvent(0, new Dictionary<string, string> { ["0"] = $"[出库] [指令错误(from):{asrsjob.TARPORT}长度验证失败]" }); return;
                }
                //出库站台号
                var stationId = int.Parse(oriport[3]);
                //入库站台对象
                var stockStation = outStockStations.FirstOrDefault(c => c.Id == stationId);
                //站台验证
                if (stockStation == null)
                {
                    infoEvent(0, new Dictionary<string, string> { ["0"] = $"[出库] [站台({stationId})查找失败]" }); return;
                }
                //出库站台状态
                var haveTrayStatus = transLineUtil.Get(stockStation.HaveTrayAddr);
                if (haveTrayStatus == (int)Success.One)
                {
                    infoEvent(0, new Dictionary<string, string> { ["0"] = $"[出库] [站台({stationId})有载]" }); return;
                }

                var to = new To();
                //排
                to.Pai = stockStation.Pai;
                //列
                to.Lie = stockStation.Lie;
                //层
                to.Ceng = stockStation.Ceng;
                //1、小板 2、大板
                #endregion

                #region write 
                var data = new Int16[8];

                data[0] = (Int16)from.Pai;
                data[1] = (Int16)from.Lie;
                data[2] = (Int16)from.Ceng;

                data[3] = (Int16)to.Pai;
                data[4] = (Int16)to.Lie;
                data[5] = (Int16)to.Ceng;

                data[6] = (Int16)asrsjob.Id;
                if (asrsjob.CTNTYPE == (int)WarehouseUnitType.小托盘)
                    data[7] = 1;
                else
                    data[7] = 2;

                //写入PLC
                var insertStatus = stackerUtil.Set(StockAddr.FromPai, 8, data);
                //休眠
                Thread.Sleep(1000);
                //流水号
                var serialNo = stackerUtil.Get(StockAddr.SerialNo);
                //排
                var fromPai = stackerUtil.Get(StockAddr.FromPai);
                //列
                var fromLie = stackerUtil.Get(StockAddr.FromLie);
                //层
                var fromCeng = stackerUtil.Get(StockAddr.FromCeng);
                //流水号
                infoEvent(0, new Dictionary<string, string> { ["0"] = $"[出库] [流水号:{serialNo} 获取堆垛{fromPai}-{fromLie}-{fromCeng}]" });
                //空闲
                if (insertStatus == (int)WorkStatus.Zero && serialNo == asrsjob.Id && fromPai > 0)
                {
                    infoEvent(0, new Dictionary<string, string> { ["0"] = $"[出库] [指令写入成功]" });

                    infoEvent(0, new Dictionary<string, string> { ["0"] = $"[出库] [托盘号:{asrsjob.CTNCODE}]" });

                    infoEvent(0, new Dictionary<string, string> { ["0"] = $"[出库] [FROM:{from.Pai}-{from.Lie}-{from.Ceng} TO:{to.Pai}-{to.Lie}-{to.Ceng}]" });
                    //更新
                    var updateResult = ASRSJOBApp.Instance.UpdateStatus(asrsjob.Id, (int)WorkStatus.One, (int)CompleteMode.自动);
                    if (updateResult.Success)
                        infoEvent(0, new Dictionary<string, string> { ["0"] = $"[出库] [更新指令状态成功]" });
                    else
                        infoEvent(0, new Dictionary<string, string> { ["0"] = $"[出库] [更新状态失败:{updateResult.Message}]" });
                }
                else
                {
                    //重试机制
                    while (serialNo == (int)WorkStatus.Zero)
                    {
                        infoEvent(0, new Dictionary<string, string> { ["0"] = $"[出库] [写入PLC失败、正在重试.]" });
                        //重新写入PLC
                        insertStatus = stackerUtil.Set(StockAddr.FromPai, 8, data);
                        //休眠
                        Thread.Sleep(1000);
                        //重新获取排
                        serialNo = stackerUtil.Get(StockAddr.SerialNo);
                        //源排
                        fromPai = stackerUtil.Get(StockAddr.FromPai);
                        //验证是否真证写入成功
                        if (insertStatus == (int)WorkStatus.Zero && serialNo == asrsjob.Id && fromPai > 0)
                        {
                            //重试
                            infoEvent(0, new Dictionary<string, string> { ["0"] = $"[出库] [重试流水号{serialNo}]" });

                            infoEvent(0, new Dictionary<string, string> { ["0"] = $"[出库] [托盘号:{asrsjob.CTNCODE}]" });

                            infoEvent(0, new Dictionary<string, string> { ["0"] = $"[出库] [指令写入成功]" });

                            infoEvent(0, new Dictionary<string, string> { ["0"] = $"[出库] [FROM:{from.Pai}-{from.Lie}-{from.Ceng} TO:{to.Pai}-{to.Lie}-{to.Ceng}]" });

                            //更新
                            var updateResult = ASRSJOBApp.Instance.UpdateStatus(asrsjob.Id, (int)WorkStatus.One, (int)CompleteMode.自动);
                            if (updateResult.Success)
                                infoEvent(0, new Dictionary<string, string> { ["0"] = $"[出库] [更新指令状态成功]" });
                            else
                                infoEvent(0, new Dictionary<string, string> { ["0"] = $"[出库] [更新状态失败:{updateResult.Message}]" });
                        }
                        //休眠
                        Thread.Sleep(2000);
                    }
                }
                #endregion
            }
            catch (Exception ex)
            {
                //界面日志
                infoEvent(0, new Dictionary<string, string> { ["0"] = $"[出库异常] [{ex.Message}]" });
            }
        }
        #endregion

        #endregion

        #region 完成

        #region 入库完成
        /// <summary>
        /// 入库完成
        /// </summary>
        public void InDone()
        {
            try
            {
                //入库完成[D103]
                var inDone = stackerUtil.Get(StockAddr.InDone);
                //验证是否入库完成
                if (inDone != (int)Success.Two)
                    return;
                infoEvent(0, new Dictionary<string, string> { ["0"] = $"[入库完成] [完成地址:{StockAddr.InDone} 值:{inDone}]" });
                //流水号
                var serialId = stackerUtil.Get(StockAddr.SerialNo);
                if (serialId <= 0)
                {
                    stackerUtil.Set(StockAddr.InDone, (Int16)0);
                    infoEvent(0, new Dictionary<string, string> { ["0"] = $"[入库完成] [无指令、清除入库信号成功]" }); return;
                }

                #region //get
                //获取指令
                var asrsjob = ASRSJOBApp.Instance.GetById(serialId);
                //指令对象是否为空
                if (asrsjob == null)
                    return;
                #endregion

                #region //write
                var resultSerialNo = stackerUtil.Set(StockAddr.SerialNo, 0);  //清空流水号
                if (resultSerialNo != 0)
                {
                    throw new Exception($"[入库完成] [清空流水号:{StockAddr.SerialNo}失败.]");
                }
                var resultInDone = stackerUtil.Set(StockAddr.InDone, 0);      //入库完成状态
                if (resultInDone != 0)
                {
                    throw new Exception($"[入库完成] [清空入库完成状态:{StockAddr.InDone}失败.]");
                }
                var resultOutDone = stackerUtil.Set(StockAddr.OutDone, 0);    //出库完成状态
                if (resultOutDone != 0)
                {
                    throw new Exception($"[入库完成] [清空出库完成状态:{StockAddr.OutDone}失败.]");
                }

                var data = new Int16[8]
                {
                    0,
                    0,
                    0,
                    0,
                    0,
                    0,
                    0,
                    0
                };
                //写入PLC
                var result = stackerUtil.Set(StockAddr.FromPai, 8, data);
                if (result == 0)
                {
                    //单据号
                    infoEvent(0, new Dictionary<string, string> { ["0"] = $"[入库完成] [单据号:{asrsjob.BINDID}]" });
                    //托盘号
                    infoEvent(0, new Dictionary<string, string> { ["0"] = $"[入库完成] [托盘号:{asrsjob.CTNCODE}]" });
                    //执行日志
                    infoEvent(0, new Dictionary<string, string> { ["0"] = $"[入库完成] [任务号:{asrsjob.Id}]" });
                    //更新指令
                    var updateResult = ASRSJOBApp.Instance.UpdateStatus(asrsjob.Id, (int)WorkStatus.Two, (int)CompleteMode.自动);
                    if (updateResult.Success)
                    {
                        //执行日志
                        infoEvent(0, new Dictionary<string, string> { ["0"] = $"[入库完成] [更新状态成功]" });
                        //界面日志
                        infoEvent(0, new Dictionary<string, string> { ["0"] = $"[入库完成] [操作流水号:{asrsjob.Id},清空流水号状态:{resultSerialNo},入库完成状态:{resultInDone},出库完成状态:{resultOutDone},清空堆垛机状态:{result}]" });
                    }
                    else
                    {
                        throw new Exception("[入库完成] [更新指令状态失败]");
                    }
                }
                else
                {
                    //界面日志
                    infoEvent(0, new Dictionary<string, string> { ["0"] = $"[入库完成] [批量写入失败]" });
                }

                //清除入库完成地址
                stackerUtil.Set(StockAddr.InDone, (Int16)0);
                //工作中
                stackerUtil.Set(StockAddr.Work, (Int16)0);
                //是否空闲
                stackerUtil.Set(StockAddr.IsFree, (Int16)2);
                #endregion
            }
            catch (Exception ex)
            {
                //界面日志
                infoEvent(0, new Dictionary<string, string> { ["0"] = $"[入库完成异常] [{ex.Message}]" });
            }
        }
        #endregion

        #region 出库完成
        /// <summary>
        /// 出库完成
        /// </summary>
        public void OutDone()
        {
            try
            {
                //出库完成[D104]
                var outDone = stackerUtil.Get(StockAddr.OutDone);
                //验证是否出库完成
                if (outDone != (int)Success.Two)
                    return;
                infoEvent(0, new Dictionary<string, string> { ["0"] = $"[出库完成] [地址{StockAddr.OutDone} 值：{outDone}]" });
                //获取序列号
                var serialId = stackerUtil.Get(StockAddr.SerialNo);
                if (serialId <= 0)
                {
                    stackerUtil.Set(StockAddr.InDone, (Int16)0);
                    infoEvent(0, new Dictionary<string, string> { ["0"] = $"[出库完成] [无指令、清除入库信号成功]" }); return;
                }

                #region // get
                //获取指令
                var asrsjob = ASRSJOBApp.Instance.GetById(serialId);
                //指令对象是否为空
                if (asrsjob == null)
                    return;

                if (string.IsNullOrEmpty(asrsjob.TARPORT))
                {
                    infoEvent(0, new Dictionary<string, string> { ["0"] = $"[出库完成] [指令错误(from):{asrsjob.ORIPORT}为空]" });return;
                }

                var oriport = asrsjob.TARPORT.Split('-');
                //验证长度
                if (oriport.Length < 4)
                {
                    infoEvent(0, new Dictionary<string, string> { ["0"] = $"[出库完成] [指令错误(from):{asrsjob.TARPORT}长度验证失败]" });return;
                }
                //站台对象
                var stationId = int.Parse(oriport[3]);
                //当前站台
                var stockStation = stockStations.FirstOrDefault(c => c.Id == stationId);
                //站台验证
                if (stockStation == null)
                {
                    infoEvent(0, new Dictionary<string, string> { ["0"] = $"[出库完成] [站台({stationId})查找失败]" });return;
                }
                #endregion

                #region // clear
                var data = new Int16[8]
                {
                            0,
                            0,
                            0,
                            0,
                            0,
                            0,
                            0,
                            0
                };
                int result = stackerUtil.Set(StockAddr.FromPai, 8, data);
                if (result == 0)
                {
                    //单据号
                    infoEvent(0, new Dictionary<string, string> { ["0"] = $"[出库完成] [单据号:{asrsjob.BINDID}]" });
                    //托盘号
                    infoEvent(0, new Dictionary<string, string> { ["0"] = $"[出库完成] [托盘号:{asrsjob.CTNCODE}]" });
                    //出库库位
                    infoEvent(0, new Dictionary<string, string> { ["0"] = $"[出库完成] [出库库位:{asrsjob.TARPORT}]" });
                    //1、流水
                    var rsultSerialNo = transLineUtil.Set(stockStation.SerialNoAddr, asrsjob.CTNCODE);
                    if (rsultSerialNo == (int)WorkStatus.Zero)
                        infoEvent(0, new Dictionary<string, string> { ["0"] = $"[出库完成] [设置流水号成功 {stockStation.SerialNoAddr}->{asrsjob.CTNCODE}]" });
                    else
                        infoEvent(0, new Dictionary<string, string> { ["0"] = $"[出库完成] [设置流水号失败 {stockStation.SerialNoAddr}->{asrsjob.CTNCODE}]" });
                    //2、站台出入库类型(入(1)、出(2)
                    var rsultStationType = transLineUtil.Set(stockStation.StationTypeAddr, (int)WorkStatus.Two);
                    if (rsultStationType == (int)WorkStatus.Zero)
                        infoEvent(0, new Dictionary<string, string> { ["0"] = $"[出库完成] [设置出库模式成功 {stockStation.StationTypeAddr}->{(int)WorkStatus.Two}]" });
                    else
                        infoEvent(0, new Dictionary<string, string> { ["0"] = $"[出库完成] [设置出库模式失败 {stockStation.StationTypeAddr}->{(int)WorkStatus.Two}]" });
                    //3、更新任务状态
                    var updateResult = ASRSJOBApp.Instance.UpdateStatus(asrsjob.Id, (int)WorkStatus.Two, (int)CompleteMode.自动);
                    if (updateResult.Success)
                        infoEvent(0, new Dictionary<string, string> { ["0"] = $"[出库完成] [更新任务状态成功]" });
                    else
                        infoEvent(0, new Dictionary<string, string> { ["0"] = $"[出库完成] [更新任务状态失败]" });
                }
                else
                    infoEvent(0, new Dictionary<string, string> { ["0"] = $"[出库完成][批量写入失败]" });

                //清除入库完成地址
                stackerUtil.Set(StockAddr.OutDone, (Int16)0);
                //工作中
                stackerUtil.Set(StockAddr.Work, (Int16)0);
                //是否空闲
                stackerUtil.Set(StockAddr.IsFree, (Int16)2);
                #endregion
            }
            catch (Exception ex)
            {
                //执行日志
                infoEvent(0, new Dictionary<string, string> { ["0"] = $"[出库完成] [异常:{ex.Message}]" });
            }
        }
        #endregion

        #endregion

        #region 重新执行
        /// <summary>
        /// 重新执行
        /// </summary>
        public JResult ReRun(int id)
        {
            var result = new JResult()
            {
                Success = false
            };

            try
            {
            }
            catch (Exception ex)
            {
                result.Message = ex.Message;
            }

            return result;
        }
        #endregion

        #region 释放资源
        /// <summary>
        /// 释放释放资源
        /// </summary>
        public void Dispose()
        {
            //堆垛机线程
            if (stackerThread != null)
                stackerThread.Abort();

            stackerThread = null;


            if (transLineUtil != null)
                transLineUtil.closeWatch();

            transLineUtil = null;

            if (stackerUtil != null)
                stackerUtil.closeWatch();

            stackerUtil = null;
        }
        #endregion

        #region 异常复位
        /// <summary>
        /// 复原
        /// </summary>
        /// <returns></returns>
        public JResult Restore()
        {
            var response = new JResult()
            {
                Success = false
            };

            try
            {
                if (stackerUtil == null)
                {
                    throw new Exception("垛机对象不存在.");
                }

                Task.Run(() => {
                    //脉冲开始
                    stackerUtil.Set(StockAddr.Restore, 1);
                    Task.Delay(3000).Wait();
                    stackerUtil.Set(StockAddr.Restore, 0);
                    //界面日志
                    infoEvent(0, new Dictionary<string, string> { ["0"] = $"[异常复原] [完成]" });
                });
            }
            catch (Exception ex)
            {
                response.Message = ex.Message;
            }

            return response;
        }
        #endregion

        #region 设置站台
        /// <summary>
        /// 设置站台
        /// </summary>
        /// <returns></returns>
        public void SetStation(int stationId, int code)
        {
            var response = new JResult()
            {
                Success = false
            };

            try
            {
                if (transLineUtil != null)
                {
                    //入库站台对象
                    var stockStation = stockStations.FirstOrDefault(c => c.Id == stationId);
                    if (stockStation == null)
                    {
                        infoEvent(0, new Dictionary<string, string> { ["0"] = $"站台({stationId})查找失败." }); return;
                    }
                    //托盘号
                    var status = transLineUtil.Set(stockStation.SerialNoAddr, code);
                    if (status == (int)WorkStatus.Zero)
                        infoEvent(0, new Dictionary<string, string> { ["0"] = $"设置流水号成功 {stockStation.SerialNoAddr}->{code}" });
                    else
                        infoEvent(0, new Dictionary<string, string> { ["0"] = $"写入输送线失败" });
                }
                else
                    infoEvent(0, new Dictionary<string, string> { ["0"] = $"[输送线对象为空]" });
            }
            catch (Exception ex)
            {
                infoEvent(0, new Dictionary<string, string> { ["0"] = $"{ex.Message}" });
            }
        }
        #endregion

        #region 设备异常
        /// <summary>
        /// 设备异常
        /// </summary>
        /// <param name="deviceExceptionCode"></param>
        /// <param name="deviceAddr"></param>
        /// <returns></returns>
        public void GetDeviceException(List<DeviceExceptionCodeResponse> deviceExceptionCode, int deviceAddr)
        {
            //地址
            var plcAddr = $"D12{deviceAddr}";
            //获取异常数据bitwise
            var bitwiseValue = stackerUtil.Get($"{plcAddr}");

            if (bitwiseValue > 0)
            {
                for (int i = 0; i < 16; i++)
                {
                    //索引移位值
                    var shiftValue = 1 << i;
                    //索引移位置比较?
                    if (shiftValue > bitwiseValue)
                    {
                        break;
                    }

                    if (((shiftValue & bitwiseValue) == shiftValue) && (shiftValue <= bitwiseValue))
                    {
                        //异常健是否存在
                        var model = deviceExceptionCode.FirstOrDefault(c => c.DeviceAddr == plcAddr && c.Indexs == i);
                        if (model != null)
                        {
                            //界面日志
                            infoEvent(90, new Dictionary<string, string> { ["0"] = $"二号垛机 {model.Message}" });
                        }
                    }
                }
            }

        }
        #endregion

        #region 清空
        /// <summary>
        /// 清空
        /// </summary>
        /// <returns></returns>
        public JResult Clear()
        {
            var response = new JResult()
            {
                Success = false
            };

            try
            {
                if (stackerUtil == null)
                {
                    throw new Exception("垛机对象不存在.");
                }

                var data = new Int16[7]
                {
                                0,
                                0,
                                0,
                                0,
                                0,
                                0,
                                0
                };

                var status = stackerUtil.Set(StockAddr.FromPai, 7, data);
                if (status == 0)
                {
                    response.Success = true;
                    response.Message = "清空垛机地址成功.";
                }
                else
                {
                    response.Success = false;
                    response.Message = "清空垛机地址失败.";
                }
            }
            catch (Exception ex)
            {
                response.Message = ex.Message;
            }

            return response;
        }
        #endregion
    }
}
